#include "Dwarf.Unmanaged.Serial.h"


using namespace std;

static DLLParseArguments pDLLParseArguments;        // method 'parseArguments' loaded from CliTools.unmanaged.dll
static DLLUsage pDLLUsage;                          // method 'usage' loaded from CliTools.unmanaged.dll
static DLLStart pDLLStart;                          // method 'start' [time count] loaded from CliTools.unmanaged.dll
static DLLStop pDLLStop;                            // method 'stop' [time count] loaded from CliTools.unmanaged.dll
static HINSTANCE hInstDll;                          // instance of CliTools.unmanaged.dll

Settings :: Settings()
{
    inputFile = new char[BUFFER_SIZE];
    resultFile = new char[BUFFER_SIZE];
    profileFile = new char[BUFFER_SIZE];

    inputFile[0] = resultFile[0] = profileFile[0] ='\0';
}

// Constructor that dispose strings.
Settings :: ~Settings()
{
    delete inputFile;
    delete resultFile;
    delete profileFile;
}

static bool getNumber(char* str, FILE* file) 
{
    short count = 0;
    char ch = ' ';
    bool result = false;

    while (!feof(file) && (ch == ' ' || ch == '\n' || ch == '\r')) 
    {
        ch = (char)getc(file);
    }

    str[count] = ch;
    while (!feof(file) && ch != '\r' && ch != '\n' && ch != ' ' && ch != ':') 
    {
        str[++count] = (ch = (char)getc(file));
        if(ch == ':'){
            result = true;
        }
    }

    str[count] = '\0';
    return result;
}

// Parse input file into solver's structures.
int Settings::parseConfigFile(Solver* solver) 
{
    FILE *file;
    // Open the file to read from.
    if( fopen_s(&file, inputFile, "rb")) 
    {
        printf("Can't open config file!\r\n");
        (pDLLUsage)();

        return -1;
    }
    char *str = new char[BUFFER_SIZE];

    int vertexIndex = 0;
    //reading the count of vertices
    if(!feof(file)) {
        getNumber(str, file);
        int length = atoi(str);
        solver->graph = new Graph(length);
        solver->graph->setHeadId(0);
    }

    //reading the count of edges
	queue<int> s;
    int previousElement = 0;
    //reading the list of vertices and edges
    while(!feof(file))
    {   
        if(getNumber(str, file))
        {      
            vertexIndex = atoi(str);
            if(vertexIndex != 0){
                 //create new array for store edges for vertex with index = previousElement
                solver->graph->edgesMatrix[previousElement] = new int[s.size()];
                solver->graph->arrayOfcounts[previousElement] = (int)s.size();
            }

            for( int i =0; !s.empty() ; i++)
            {
                solver->graph->edgesMatrix[previousElement][i] = s.front();
                s.pop();
            }
            previousElement = vertexIndex;
        } 
        else if (str[0] != '\0')
        {   //write the edge and add to queue of edges
            int index = atoi(str);
            s.push(index);
        }
    }    

    //read last element
    solver->graph->edgesMatrix[previousElement] = new int[s.size()];
    solver->graph->arrayOfcounts[previousElement] = (int)s.size();

    for( int i =0; !s.empty() ; i++)
    {
        solver->graph->edgesMatrix[vertexIndex][i] = s.front();
        s.pop();
    }

    fclose(file);

    return 0;
}

int Settings::init(int argc, char** argv, Solver *solver) 
{

    // Load the dll into the address space
    hInstDll = LoadLibraryA("CliTools");
    if(!hInstDll)
    {
        printf("Error while loading CliTools.dll.");
        return -1;
    }

    // Retrieve a pointer to the factory function
    pDLLParseArguments = 
        (DLLParseArguments) GetProcAddress(hInstDll, "parseArguments");

    pDLLUsage =
        (DLLUsage) GetProcAddress(hInstDll, "usage");

    pDLLStart = 
        (DLLStart) GetProcAddress(hInstDll, "start");

    pDLLStop = 
        (DLLStop) GetProcAddress(hInstDll, "stop");

    if ((pDLLParseArguments)(argc, argv, &inputFile, &profileFile,  &resultFile)) 
    {
        FreeLibrary(hInstDll); 
        return -1;
    }
    //Parse input file. 
    if (parseConfigFile(solver)) 
    {
        FreeLibrary(hInstDll); 
        return -1;
    }
    //Write settings if it's needed.
    writeSettings(solver);
    return 0;
}

// Start the time count.
void Settings::start() 
{
    (pDLLStart)();
}

void Settings::finish(Solver *solver)
{
    double time;
    (pDLLStop)(&time);
    //Release dll.
    FreeLibrary(hInstDll);

    printf("\r\n");
    printf("Clock time (sec): %.8f \r\n", time);  

    FILE* outputResult = 0;
    FILE* outputProfile = 0;

    if( fopen_s(&outputProfile, profileFile , "wb")) 
    {
        printf("Can't create profile file!\r\n");            
        fclose(outputProfile);
        return;
    }

    if( fopen_s(&outputResult, resultFile , "wb")) 
    {
        printf("Can't create result file!\r\n");   
        fclose(outputResult);
        return;
    }


    fprintf(outputProfile, "#Dwarf name:%s\r\n", DWARF_NAME);

    char tmpbuf[BUFFER_SIZE];
    _tzset();

    _strdate_s( tmpbuf, BUFFER_SIZE );
    fprintf(outputProfile, "#Time: %s ",tmpbuf);

    _strtime_s( tmpbuf, BUFFER_SIZE );
    fprintf(outputProfile, "%s \r\n",tmpbuf);

    fprintf(outputProfile, "#Length: %i\r\n", solver->graph->getLength());
    fprintf(outputProfile, "#Result time (sec): %.8f\r\n", time);
    fprintf(outputResult,  "#Result domain:\r\n");
    Graph *g = solver->graph;
    int length = g->getLength();
    //write to file the list of vertices and edges
    for (int i = 0; i < length; i ++) 
    {
        fprintf(outputResult,"%i :" ,i); 
        fprintf(outputResult,g->edgesToString(i).c_str());
        fprintf(outputResult, "\r\n");
    } 

    fprintf(outputResult, "\r\n");
    fprintf(outputProfile, "#eof");

    fclose(outputProfile);
    fclose(outputResult);

}

// Write settings.
void Settings::writeSettings(Solver *solver)
{   
    // Write general settings.
    printf("Kernel settings summary  : \r\n");
    printf("Dwarf name               : %s \r\n", DWARF_NAME);
    printf("Length                  : %d \r\n", solver->graph->getLength());
    printf("Input file              : %s \r\n", inputFile);
    printf("Profile file            : %s \r\n", profileFile);    
    printf("Result file             : %s \r\n", resultFile);    
    printf("\r\n");
}